#include <assert.h>
#include <ctype.h>
#include <locale.h>
#include <errno.h>
#include <float.h>
#include <inttypes.h>
#include <limits.h>
#include <math.h>
#include <pthread.h>
#include <setjmp.h>
#include <signal.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdnoreturn.h>
#include <string.h>
#include <time.h>
#include<windows.h>

void sleep(int);


int* beyond_variable_lifetime(int bar)
{
    printf("***** beyond_variable_lifetime *****\n");
    //  Returning reference to variable declared static is defined behaviour, 
    // as the variable is not destroyed after leaving current scope.
    static int baz = 6;
    baz += bar;
    return &baz;  /* (&baz) copied to new memory location outside of foo.
                warning: function returns address of local variable [-Wreturn-local-addr]
                 */

/* (1) The lifetime of baz and bar end here as they have automatic storage
* duration (local variables), thus the returned pointer is not valid! */
}

void copying_overlapping_memory(void)
{
    printf("***** copying_overlapping_memory *****\n");
    /*
    A wide variety of standard library functions 
    have among their effects copying byte sequences from one memory region to another.
    各种标准库函数的作用之一是将字节序列从一个区域复制到另一个区域

    Most of these functions have undefined behavior when the source and destination regions overlap

                overlapping area
                |
                _ _
                | |
                v v
    T h i s i s  a n e x a m p l e \0
    ^           ^
    |           |
    |           destination
    |
    source

    undefined: mong the standard library functions with a limitation of this kind are memcpy(), strcpy(), strcat(), sprintf(), and sscanf(). 

    * If copying takes place between objects that overlap, the behavior is undefined.

    defined: The memmove() function is the principal exception to this rule. Its definition specifies that the function behaves as if
the source data were first copied into a temporary buffer and then written to the destination address. 
    */
   char str[19] = "This is an example";
   printf("before memcpy str: %s \n", str);
   memcpy(str + 7, str, 10);
   printf("after memcpy str: %s \n", str);

}


void signed_integer_overflow(void)
{
    printf("***** signed_integer_overflow *****\n");
    /*
    Unsigned integer arithmetic does not overflow because paragraph 6.2.5/9 applies, 
    causing any unsigned result that otherwise would be out of range to be reduced to an in-range value. 
    无符号整数算术不会溢出，因为第 6.2.5/9 段适用，导致任何否则超出范围的无符号结果被减少到范围内值。

    There is no analogous provision for signed integer types, however; these can and do overflow, producing undefined behavior. 
    然而，对于有符号整数类型没有类似的规定；这些可以并且确实溢出，产生未定义的行为
    */
   int i = INT_MAX + 1; /* Overflow happens here */
   printf("INT_MAX: %d, INT_MAX + 1: %d\n", INT_MAX, i);
}

void uninitialized_variable(void)
{
    printf("***** uninitialized_variable *****\n");
    /*
    Automatic variables which are not initialized have indeterminate values; 
    accessing these can lead to undefined behavior.
    */
    int a;
    printf("uninitialized a: %d\n", a);
    /*
    Note: Variables with static or thread local storage, 
    including global variables without the static keyword, 
    are initialized to either zero, or their initialized value. 
    */
    static int b;
    printf("initialized b: %d\n", b);

    int *p;
    p++; // Trying to increment an uninitialized pointer.
    printf("uninitialized *p: %p\n", p);

    static int *p1;
    int int_p1 = 2;
    p1++; // Trying to increment an uninitialized pointer.
    p1 = &int_p1;
    printf("initialized p1: %p\n *p1: %d\n", p1, *p1);
}



// global variables
int a = 0;

void* data_race(void* ignore)
{
    printf("***** data_race *****\n");
    a = 1;
    printf("a in data_race: %d\n", a);
    // return 0;

}


void incorrect_format_specifier_in_printf(void)
{
    printf("***** incorrect_format_specifier_in_printf *****\n");
    long z = "B";
    printf("%c\n", z);

    printf("%f\n", 0); // %f expects double. However 0 is of type int.
    // Above line of code is undefined behavior. 
    
}

void modify_string_literal(void)
{
    printf("***** modify_string_literal *****\n");
    /*
    the char pointer p is initialized to the address of a string literal. 
    Attempting to modify the string literal has undefined behavior.
    */
    char *p = "hello world";
    // p[0] = 'H'; // Undefined behavior
    printf("*p: %c\n", *p);
    /*
    However, modifying a mutable array of char directly, 
    or through a pointer is naturally not undefined behavior, 
    even if its initializer is a literal string. 
    */
   char a[] = "hello world";
   char *p1 = a;
   a[0] = 'H';
   p1[7] = 'W';
   printf("*p1: %c\n", *p1);
   // That's because the string literal is effectively copied to the array each time the array is initialized 
}

void passing_null_pointer_to_printf_s_conversion(void)
{
    printf("***** passing_null_pointer_to_printf_s_conversion *****\n");
    char *foo = NULL;
    // A null pointer does not point to the initial element of any array of character type
    printf("foo: %s\n", foo); /* undefined behavior */
    /*
    However, the undefined behavior does not always mean that the program crashes
     — some systems take steps to avoid the crash 
     that normally happens when a null pointer is dereferenced. 
     For example Glibc is known to print
        (null)
    */
    char *foo1 = 0;
    printf("foo1: %s\n", foo1); /* undefined behavior */

    /*
    Note that null pointer is different from an empty string. 
    So, the following is valid and has no undefined behaviour. 
    It'll just print a newline:
    */
    char *foo2 = "";
    printf("foo2: %s\n", foo2);
}

void modifying_more_than_once_between_two_sequence_points(void)
{
    printf("***** modifying_more_than_once_between_two_sequence_points *****\n");
    int i = 42;
    printf("i: %d\n", i);
    i = i++; /* Assignment changes variable, post-increment as well */
    printf("i++: %d\n", i);
    int a = i++ + i--; // 85
    printf("i++ + i--: %d\n", a);

    i = 42;
    // sequencing point rules
    i = (i++, i+42); /* The comma-operator creates a sequence point */
    printf("(i++, i+42): %d\n", i); // 85

    i = 42;
    printf("%d %d\n", i++, i++); // 43 42 /* commas as separator of function arguments are not comma-operators */        
}

void freeing_memory_twice(void)
{
    printf("***** freeing_memory_twice *****\n");
    int *x = malloc(sizeof(int));
    *x = 9;
    printf("*x: %d\n", *x); // 9
    free(x);
    free(x);
    printf("*x: %d\n", *x); // 43653928
}

/*
If the value of the right operand is negative 
or is greater than or equal to the width of the promoted left operand, 
the behavior is undefined.
*/
void bit_shift(void)
{
    printf("***** bit_shift *****\n");
    // // If the shift count value is a negative value then both left shift and right shift operations are undefined
    // int x = 5 << -3; /* undefined */
    // int x = 5 >> -3; /* undefined */
    // // If left shift is performed on a negative value, it's undefined:
    // int x = -5 << 3; /* undefined */
    // // If left shift is performed on a positive value 
    // // and result of the mathematical value is not representable in the type, it's undefined
    
    // /* Assuming an int is 32-bits wide, the value '5 * 2^72' doesn't fit
    // * in an int. So, this is undefined. */
    // int x = 5 << 72;
}

// A function that's declared with _Noreturn (or noreturn) is not allowed to return to its caller. 
// If such a function does return to its caller, the behavior is undefined.
// noreturn void function_declared_with_noreturn(void);

// void function_declared_with_noreturn(void)
// {
//     printf("***** function_declared_with_noreturn *****\n");
// } /* Undefined behavior as func() returns */

// noreturn void my_exit(void);
/* calls exit() and doesn't return to its caller. */
void my_exit(void)
{
    printf("***** my_exit *****\n");
    system("pause");
    // exit(0);
}

void accessing_memory_beyond_allocated_chunk(void)
{
    printf("***** accessing_memory_beyond_allocated_chunk *****\n");
    int array[3];
    int *beyond_array = array + 3;
    *beyond_array = 0; /* Accesses memory that has not been allocated. */

    int array_1[3];
    array_1[3] = 0; // outside of that range results in undefined behavior
/*
Note that pointing past the last element of an array is not undefined behavior 
请注意，指向数组的最后一个元素不是未定义的行为
(beyond_array = array + 3 is well defined here), 

but dereferencing it is (*beyond_array is undefined behavior). 
但解引用是未定义的行为

This rule also holds for dynamically allocated memory (such as buffers created through malloc).
此规则也适用于动态分配的内存
*/
}

void modifying_const_variable_using_pointer(void)
{
    printf("***** modifying_const_variable_using_pointer *****\n");
    const int foo_readonly = 10;
    printf("foo_readonly: %d\n", foo_readonly); // 10
    int *foo_ptr;
    foo_ptr = (int *)&foo_readonly; /* (1) This casts away the const qualifier 这抛弃了 const 限定符*/
    *foo_ptr = 20; /* This is undefined behavior */
    printf("*foo_ptr: %d\n", *foo_ptr); // 20
}
/*
读取没有内存支持的未初始化对象
Reading an object will cause undefined behavior, if the object is:
    uninitialized
    defined with automatic storage duration
    it's address is never taken

*/
void reading_uninitialized_obj_that_not_backed_by_memory(void)
{
    printf("***** reading_uninitialized_obj_that_not_backed_by_memory *****\n");
    int a;
    printf("a: %d\n", a); // 6422264
    int b = a;
    printf("b: %d\n", b); // 6422264

}

void pointer_not_properly_bounded(void)
{
    printf("***** pointer_not_properly_bounded *****\n");
    char buffer[6] = "hello";
    printf("buffer: %s\n", buffer);
    char *ptr1 = buffer - 1; /* undefined behavior */
    printf("buffer - 1: %c\n", *ptr1);
    char *ptr12 = buffer + 4; // o  /* OK, pointing to the '\0' inside the array */
    printf("buffer + 4: %c\n", *ptr12);
    char *ptr2 = buffer + 5; /* OK, pointing to the '\0' inside the array */
    printf("buffer + 5: %c\n", *ptr2);
    char *ptr3 = buffer + 6; /* OK, pointing to just beyond */
    printf("buffer + 6: %c\n", *ptr3);
    char *ptr4 = buffer + 7; /* undefined behavior */
    printf("buffer + 7: %c\n", *ptr4);

    char *ptr33 = buffer + 6; /* OK, pointing to just beyond */
    char value = *ptr33; /* undefined behavior */
    printf("buffer + 6: %c\n", *ptr33);
}

void dereferenceing_null_pointer(void)
{
    printf("***** dereferenceing_null_pointer *****\n");
    /*
    A NULL pointer is guaranteed by the C standard to compare unequal to any pointer to a valid object, 
    and dereferencing it invokes undefined behavior.
    */
    int * pointer = NULL;
    // int value = *pointer; /* Dereferencing happens here */
    // printf("value: %d\n", value);
    printf("pointer: %p\n", pointer);
}

void using_fflush_input_stream(void)
{
    printf("***** using_fflush_input_stream *****\n");
    int i;
    const int SIZE = 4096;
    char input[SIZE];
    printf("Please input a string: ");
    scanf("%i", &i);
    gets(input);
    printf("input: %s\n", input);
    // There is no standard way to discard unread characters from an input stream. 
    // On the other hand, some implementations uses fflush to clear stdin buffer.
    fflush(stdin); // <-- undefined behavior
    gets(input);
    puts(input);
    printf("input: %s\n", input);
}


void inconsistent_linkage_of_identifiers(void)
{
    printf("***** inconsistent_linkage_of_identifiers *****\n");
    // /*
    // If, within a translation unit, 
    // the same identifier appears with both internal and external linkage, 
    // the behavior is undefined
    // */
    // extern int var;
    // static int var; /* Undefined behaviour */

    // // Note that if an prior declaration of an identifier is visible then it'll have the prior declaration's linkage.

    // /* 1. This is NOT undefined */
    // static int var;
    // extern int var;
    // /* 2. This is NOT undefined */
    // static int var;
    // static int var;
    // /* 3. This is NOT undefined */
    // extern int var;
    // extern int var;
}

void missing_return_statement_in_value_returning_function(void)
{
    printf("***** missing_return_statement_in_value_returning_function *****\n");
    /* do stuff */
    /* no return here */
}

void conversion_between_pointer_types(void)
{
    printf("***** conversion_between_pointer_types *****\n");
    // The following might have undefined behavior due to incorrect pointer alignment:
    char *memory_block = calloc(sizeof(uint32_t) + 1, 1);
    uint32_t *intptr = (uint32_t*)(memory_block + 1); /* possible undefined behavior */
    uint32_t mvalue = *intptr;

    /*
    Casting char * to pointers to other types without any concern to alignment requirements is sometimes incorrectly
    used for decoding packed structures such as file headers or network packets
    */

   // You can avoid the undefined behavior arising from misaligned pointer conversion by using memcpy:
   memcpy(&mvalue, memory_block + 1, sizeof mvalue);
/*
This copy operation for our example only leads to valid value of mvalue because:
    We used calloc, so the bytes are properly initialized. In our case all bytes have value 0, but any other proper
    initialization would do.
    uint32_t is an exact width type and has no padding bits
    Any arbitrary bit pattern is a valid representation for any unsigned type.

*/
}

void modifying_stringing_returned_by_function(void)
{
    printf("***** modifying_stringing_returned_by_function *****\n");
    // Modifying the string returned by getenv, strerror, and setlocale functions
    /*
    Modifying the strings returned by the standard functions getenv(), strerror() and setlocale() is undefined.
    So, implementations may use static storage for these strings.

    */
}



void main(void)
{
    int* p;

    p = beyond_variable_lifetime(5); /* (2) this expression's behavior is undefined */
    *p = *p - 6; /* (3) Undefined behaviour here */
    printf("%d\n", *p);

    copying_overlapping_memory();

    signed_integer_overflow();

    uninitialized_variable();

    /*
    The execution of a program contains a data race if it contains two conflicting actions in different threads, 
    at least one of which is not atomic, and neither happens before the other. 
    Any such data race results in undefined behavior.
    */
    pthread_t id;
    pthread_create(&id, NULL, data_race, NULL);
    sleep(1);
    int b = a;
    printf("b in main: %d\n", b);
    // Wait for foo() and retrieve value in ptr;
    pthread_join(id, NULL);

    incorrect_format_specifier_in_printf();

    modify_string_literal();

    passing_null_pointer_to_printf_s_conversion();

    modifying_more_than_once_between_two_sequence_points();

    freeing_memory_twice();

    // function_declared_with_noreturn();
    my_exit();

    accessing_memory_beyond_allocated_chunk();

    modifying_const_variable_using_pointer();

    reading_uninitialized_obj_that_not_backed_by_memory();
    
    pointer_not_properly_bounded();

    dereferenceing_null_pointer();

    using_fflush_input_stream();

    /* Trying to use the (not) returned value causes UB */
    // int value = missing_return_statement_in_value_returning_function();
    /* The value (not) returned from foo() is unused. 
    * So, this program doesn't cause *undefined behaviour*. */
    missing_return_statement_in_value_returning_function();

    conversion_between_pointer_types();

    modifying_stringing_returned_by_function();
    system("pause");
}
